<?php
class gMblog {
	/**
	 * 计划任务:更新状态
	 * @return mixed 未完成返回-1
	 */
	public static function cronUpdate() {
		$M = M('mblog_profile');
		$key = substr(md5(C('SITE_KEY').'mblogcron'),0,8);
		$uids = S($key.'cron_mblog');
		if(!$uids) {//init
			$uids = $M->field('uid')->distinct(true)->select();
		}
		if($t = array_shift($uids)) {
			self::update($t['uid']);
			if(count($uids) > 0) {
				S($key.'cron_mblog', $uids);
				return -1;
			}
		}
		return;
	}
	/**
	 * 自动更新状态
	 * @param int $uid 用户UID
	 * @return mixed 失败原因array or true
	 */
	public static function update($uid) {
		$M = M('mblog_profile');
		$profiles = $M->where(array('uid'=>$uid))->select();
		$errors = array();
		foreach($profiles as $p) {
			if($p['action']!='r'||$p['uid']!=$uid) continue;
			$data = unserialize($p['data']);
			if($data['since']>0) {
				$arr = self::readUser($p['type'], $p['mblog_user'], $data['since']);
				$arr = array_reverse($arr);
				foreach($arr as $v) {
					if($v['text'][0]['VALUE']{0}=='@') continue;//跳过@消息
					$e = self::batchSend($uid, $v['text'][0]['VALUE'], $profiles, 'w');
					if($e !== true) $errors = array_merge($errors, $e);
				}
			} else {
				$arr = self::readUser($p['type'], $p['mblog_user']);
			}
			if(is_array($arr) && $arr[0]['id'][0]['VALUE']>0) {
				$data['since'] = $arr[0]['id'][0]['VALUE'];
				$p['data'] = serialize($data);
				$M->save($p);
			}
		}
		return $errors ? $errors : true;
	}
	/**
	 * 批量发送某个用户记为同步更新的围脖
	 * @param int $uid 用户UID
	 * @param string $msg 消息
	 * @param array $profiles = '' UID的profiles数组,留空时从数据库中查询
	 * @param string $action = 'u' profile的action过滤
	 * @return mixed 失败原因array or true
	 */
	public static function batchSend($uid, $msg, $profiles = '', $action = 'u') {
		$errors = array();
		is_array($profiles) or $profiles = M('mblog_profile')->where(array('uid'=>$uid,'action'=>$action))->select();
		foreach($profiles as $p) {
			if( $p['action']!==$action||$p['uid']!=$uid ) continue;
			$e = self::sendMsg($p, $msg);
			if($e !== true) {
				$errors[] = "{$p['type']}-{$p['mblog_user']}:$e";
			}
		}
		return $errors ? $errors : true;
	}
	
	private static $send_api_urls = array(
		'digu'		=>'http://api.minicloud.com.cn/statuses/update.xml',
		'twitter'	=>'http://twitter.com/statuses/update.xml',
		'9911'		=>'http://api.9911.com/statuses/update.xml',
		'zuosa'		=>'http://api.zuosa.com/statuses/update.xml',
		'renjian'	=>'http://api.renjian.com/statuses/update.xml',
		'follow5'	=>'http://api.follow5.com/api/statuses/update.xml?api_key=18C3632483473B8D86419231675C86DA',
	);
	/**
	 * 发送状态
	 * @param array $profile 围脖服务信息数组
	 * @param string $msg 状态
	 * @return mixed 失败原因 or true
	 */
	public static function sendMsg($profile, $msg) {
		$type = $profile['type'];
		$api = self::$send_api_urls[$type];
		switch($type) {
			case 'digu':
			case 'twitter':case '9911':case 'zuosa':case 'follow5':
			case 'renjian':
				$api = parse_url($api);
				$data = unserialize($profile['data']);
				//prepare data
				$GALX_VER = C('GALX_VER');
				$user = $profile['mblog_user'];
				$pass = galaxy::authcode($data['pass'], 'DECODE', 0, C('SITE_KEY').'mblog');
				//$pass = $data['pass'];
				$auth = base64_encode("$user:$pass");
				if($type=='digu') {
					$content = "source=Galaxy&content=".urlencode($msg);
				} elseif($type=='renjian') {
					$content = "source=Galaxy&text=".urlencode($msg);
				} else {
					$content = "source=Galaxy&status=".urlencode($msg);
				}
				$contentLength = strlen($content);
				
				//build connection
				$fp = fsockopen($api['host'], 80);
				if(!$fp) return 'mblog_connection_error';
			    stream_set_timeout($fp, 2);
			    //POST data
				fwrite($fp, "POST {$api['path']} HTTP/1.0\r\n");
			    fwrite($fp, "Authorization: Basic {$auth}\r\n");
			    fwrite($fp, "User-Agent: Mozilla/5.0 (compatible; Galaxy/".C("GALX_VER").";)\r\n");
			    fwrite($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
			    fwrite($fp, "Content-Length: $contentLength\r\n");
			    fwrite($fp, "\r\n");
			    fwrite($fp, $content);
			    //get the response
			    $response = '';
			    do {
				    $line = fgets($fp);
				    $response .= $line;
				    $info = stream_get_meta_data($fp);
			    } while(!$info['eof'] && !$info['timed_out']);
				fclose($fp);
				if ($info['timed_out']) return 'mblog_read_timeout';
				$lines = explode("\r\n", $response);
				return strpos($lines[0], 'HTTP/1.1 200')!==false ? true : $lines[0];
			    break;			
			default:
				return 'mblog_unknown_type';
				break;
		}
	}
	private static $read_api_urls = array(
		'digu'		=>'http://api.minicloud.com.cn/statuses/user_timeline.xml?userIdOrName=[%user]&since_id=[%since]',
		'twitter'	=>'http://twitter.com/statuses/user_timeline/[%user].xml?since_id=[%since]',
		'zuosa'		=>'http://api.zuosa.com/statuses/user_timeline/[%user].xml?since_id=[%since]',
	);
	/**
	 * 读取用户时间线
	 * @param string $type 围脖服务类型
	 * @param string $username 用户名
	 * @param int $since since_id
	 * @return mixed 失败原因  or XML解析后的Array
	 */
	public static function readUser($type, $user, $since = 0) {
		$api = self::$read_api_urls[$type];
		switch($type) {
			case 'digu':
			case 'twitter':
				$api = str_replace('[%user]', $user, $api);
				$api = str_replace('[%since]', $since, $api);
				$api = parse_url($api);
				//build connection
				$fp = fsockopen($api['host'], 80);
				if(!$fp) return 'mblog_connection_error';
			    fwrite($fp, "GET {$api['path']}?{$api['query']} HTTP/1.0\r\n");
			    fwrite($fp, "Host: {$api['host']}\r\n");
			    fwrite($fp, "User-Agent: Mozilla/5.0 (compatible; Galaxy/".C("GALX_VER").";)\r\n");
			    fwrite($fp, "\r\n");
			    stream_set_timeout($fp, 2);
			    
			    //get the response
			    $xml = '';
			    $body_begin = false;
			    do {
				    $line = fgets($fp);
				    if($body_begin) $xml .= $line;
				    if(!trim($line)) $body_begin = true;
				    $info = stream_get_meta_data($fp);
			    } while(!$info['eof'] && !$info['timed_out']);
				fclose($fp);
				if ($info['timed_out']) return 'mblog_read_timeout';
				//try to parse the xml
				try {
					$xp = new XMLParser();
					$result = $xp->getXMLTree($xml);
					return $result['statuses'][0]['status'];
				} catch(Exception $e) {
					return 'mblog_read_parse_error';
				}
				break;
			default:
				return 'mblog_unknown_type';
				break;
		}
	}
}
?>